﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PSIEpics;
using System.Collections;
using string_short;
using jtext_EpicsClient;
using System.Text.RegularExpressions;
using System.Threading;

namespace jtext_EpicsClient
{
    //this is a simple class
    //it provide simple way to access PVs
    //thanks to the PSIEpics lib the arrary record and normal record is the same object it really simplified the code


    /// <summary>
    ///This is a simple class..
    ///It provide simple way to access PVs..
    ///Thanks to the PSIEpics lib the arrary record and normal record is the same object it really simplified the code 
    ///most of the work about pv is still done by PSIEpics.channel,so get really familiar with it.
    /// </summary>
    public class JECLient
    {
        /// <summary>
        /// It use the PSIEpics.EpicsClient to make epics channels that associated with PV 
        /// </summary>
        public EpicsClient MyClient;

        //EpicsClient MyClient2;  //used by Record Value
        /// <summary>
        /// The channles is kept here.
        /// <para>although it's public i really dont recommand you touch this ,it will be protected one day i promise
        /// </summary>
        public Hashtable ChannelList;
        /// <summary>
        /// know nothing about channels?OK used this ,just pass a pvName the value is back,and set is working as well.
        /// <seealso cref="RecordValue"/>
        /// </summary>
        public RecordValue RecordVAL;  //use this you can set and get the value of a pv with out deal with the channel




        /// <summary>
        /// PV that are Failed to connected to the server
        /// </summary>
        //private ArrayList FailList;

        ///// <summary>
        ///// keeeps tht <T> for each channels when reload the channel you will need it
        ///// </summary>
        ////private Hashtable ChannelType;

        //private Hashtable ChannelSvaerList;

        ///// <summary>
        ///// auto recover the failed connections
        ///// </summary>
        //public bool AutoRecover;

        ////recover pv at a time base
        ////private TimerCallback RecoverDelegate;
        ////private Timer RecoverTimer;

        /// <summary>
        /// Initializes a new instance of the <see cref="JECLient"/> class.
        /// </summary>
        public JECLient()
        {
            MyClient = new EpicsClient();
            //MyClient2 = new EpicsClient();
            ChannelList = new Hashtable();
            RecordVAL = new RecordValue(this);
            //ChannelType = new Hashtable();
            //FailList = new ArrayList();
            //ChannelSvaerList = new Hashtable();
            //AutoRecover = true;
            //IniRecover();
        }


        /// <summary>
        /// Adds an epics channel.
        /// </summary>
        /// <param name="PvName">Name of the pv.</param>
        public void AddChannel(string PvName)
        {
            if (ChannelList.Contains(PvName) == true)
            {
                //throw new System.Exception("Channel already created");
                return;
            }
            else
            {
                EpicsChannel TChannel = MyClient.CreateChannel(PvName);
                ChannelList.Add(PvName, TChannel);
                //ChannelSaver tChannelsaver = new ChannelSaver();
                //tChannelsaver.PVName = PvName;
                //if (ChannelSvaerList.Contains(PvName) == false)
                //{
                //    ChannelSvaerList.Add(PvName, tChannelsaver);
                //}
                //TChannel.StatusChanged += new EpicsStatusDelegate(ConnectionFailed);
            }
        }

        /// <summary>
        /// Adds the channel.
        /// </summary>
        /// <typeparam name="T">Data type</typeparam>
        /// <param name="PvName">Name of the pv.</param>
        public void AddChannel<T>(string PvName)
        {
            if (ChannelList.Contains(PvName) == true)
            {
                //throw new System.Exception("Channel already created");
                return;
            }
            else
            {
                EpicsChannel TChannel = MyClient.CreateChannel<T>(PvName);
                ChannelList.Add(PvName, TChannel);
                ////ChannelType.Add(PvName, typeof(T));
                //ChannelSaver tChannelsaver=new ChannelSaver();
                //tChannelsaver.PVName=PvName;
                //tChannelsaver.myType=typeof (T);
                //if (ChannelSvaerList.Contains(PvName) == false)
                //{
                //    ChannelSvaerList.Add(PvName, tChannelsaver);
                //}
            }
        }

        /// <summary>
        /// Disposes the channel.
        /// </summary>
        /// <param name="PvName">Name of the pv.</param>
        public void DisposeChannel(string PvName)
        {
            ((EpicsChannel)ChannelList[PvName]).Dispose();
            ChannelList.Remove(PvName);
        }

        /// <summary>
        /// Disposes all channels on this client.
        /// </summary>
        public void DisposeAll()
        {
            foreach (DictionaryEntry channel in ChannelList)
            {
                ((EpicsChannel)(channel.Value)).Dispose();
            }
            ChannelList.Clear();
        }

        /// <summary>
        /// Gets the <see cref="PSIEpics.EpicsChannel"/> with the specified pv name.
        /// <para>with the channel you can do much more ,like monitor or get severity</para>
        /// </summary>
        public EpicsChannel this[string PvName]
        {
            get
            {
                return ((EpicsChannel)ChannelList[PvName]);
            }

        }


        #region Autorecover
        ///// <summary>
        ///// when a pv failed added to the connection fail list
        ///// </summary>
        ///// <param name="sender">The sender.</param>
        ///// <param name="newStatus">The new status.</param>
        //private void ConnectionFailed(EpicsChannel sender,	ChannelStatus newStatus)
        //{
        //    if (AutoRecover == false)
        //    {
        //        return;
        //    }
        //    if (newStatus == ChannelStatus.DISCONNECTED)
        //    {
        //        //sender.Dispose();
        //        if (sender.MonitorChanged != null)
        //        {
        //            ((ChannelSaver)(ChannelSvaerList[sender.ChannelName])).MonitorHandler = sender.MonitorChanged;
        //        }
        //        FailList.Add(sender.ChannelName);
        //    }
        //}

        ///// <summary>
        ///// try Reconnects pv that failed.
        ///// </summary>
        //private void ReconnectFailed()
        //{
        //    if (AutoRecover == false)
        //    {
        //        return;
        //    }
        //    foreach (string Tpv in FailList)
        //    {
        //        DisposeChannel(Tpv);
        //        if ( ((ChannelSaver)(ChannelSvaerList[Tpv])).myType!=null )
        //        {
        //            Type myT= ((ChannelSaver)(ChannelSvaerList[Tpv])).myType;
        //            this.AddChannel<myT.GetType>(Tpv);
        //        }
        //        else
        //        {
        //            this.AddChannel(Tpv);
        //        }
        //        //recover the monitor 
        //        if (((ChannelSaver)(ChannelSvaerList[Tpv])).MonitorHandler != null)
        //        {
        //            this[Tpv].MonitorChanged += ((ChannelSaver)(ChannelSvaerList[Tpv])).MonitorHandler;
        //        }

        //    }
        //}

        ///// <summary>
        ///// Inis the recover thread.
        ///// </summary>
        //private void IniRecover()
        //{
        //    RecoverTimer = new Timer(RecoverDelegate, null, 1000, 1000);
        //}

        #endregion


        #region string byte

        /// <summary>
        /// Gets the string.  If the channel(PV) is a string(acturly a array)use this to get it,Very use for.
        /// </summary>
        /// <param name="PvName">Name of the pv.</param>
        /// <returns></returns>
        public string GetString(string PvName)
        {
            EpicsChannel TChannel = ((EpicsChannel)ChannelList[PvName]);
            sbyte[] msg = TChannel.Get<sbyte[]>();
            string myStr = Str_Short.SByteToString(msg);
            string[] myStrs = Regex.Split(myStr, "\0");
            return myStrs[0];
        }


        /// <summary>
        /// Gets the string.  If the channel(PV) is a string(acturly a array)use this to get it,Very use for.
        /// </summary>
        /// <param name="PvName">Name of the pv.</param>
        /// <param name="len">The lenth of the string.</param>
        /// <returns></returns>
        public string GetString(string PvName, int len)
        {
            EpicsChannel TChannel = ((EpicsChannel)ChannelList[PvName]);
            sbyte[] msg = TChannel.Get<sbyte[]>(len);
            return Str_Short.SByteToString(msg);
        }


        /// <summary>
        /// Gets the byte array.
        /// </summary>
        /// <param name="PvName">Name of the pv.</param>
        /// <param name="len">The lenth of the byte[].</param>
        /// <returns></returns>
        public byte[] GetByte(string PvName, int len)
        {
            //len is the lenth in short
            EpicsChannel TChannel = ((EpicsChannel)ChannelList[PvName]);
            sbyte[] msg = TChannel.Get<sbyte[]>(len);
            return Str_Short.SByteToByte(msg);
        }

        /// <summary>
        /// Gets the byte.   With this you don't have to speciefy the lenth,but this is slower.
        /// </summary>
        /// <param name="PvName">Name of the pv.</param>
        /// <returns></returns>
        public byte[] GetByte(string PvName)
        {
            //return the full array
            EpicsChannel TChannel = ((EpicsChannel)ChannelList[PvName]);
            sbyte[] msg = TChannel.Get<sbyte[]>();
            return Str_Short.SByteToByte(msg);
        }

        /// <summary>
        /// Sets the string.
        /// <para>put the string message to this channel(pv)</para>
        /// </summary>
        /// <param name="PvName">Name of the pv.</param>
        /// <param name="Message">The message.</param>
        /// <returns></returns>
        public int SetString(string PvName, string Message)
        {
            //return  the lenth of the short 
            EpicsChannel TChannel = ((EpicsChannel)ChannelList[PvName]);
            sbyte[] msg = Str_Short.StringToSByte(Message);
            TChannel.Put(msg);
            return msg.Length;

        }

        /// <summary>
        /// Sets the byte.
        /// <para>put the byte[] message to this channel(pv)</para>
        /// </summary>
        /// <param name="PvName">Name of the pv.</param>
        /// <param name="Message">The message.</param>
        /// <returns></returns>
        public int SetByte(string PvName, byte[] Message)
        {
            //return  the lenth of the short 
            EpicsChannel TChannel = ((EpicsChannel)ChannelList[PvName]);
            sbyte[] msg = Str_Short.ByteToSByte(Message);
            TChannel.Put(msg);
            return msg.Length;

        }

        #endregion



    }

 

}
